#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <assert.h>

#include "base/simapi.h"

// Testcase for pthread when going back and forth real mode and sim mode

void* run_test(void *unused) {
    pthread_key_t key;
    pthread_t tid;

    assert(inSimulation);

    tid = pthread_self();
    goto_real();
    printf("in real tid(%i)\n", (int)tid);
    pthread_key_create(&key, NULL);
    int private_val=42;
    pthread_setspecific(key, &private_val);
    void *value = pthread_getspecific(key);
    if (*((int*)value) == 42) {
        printf("[real] thread-private value maches 42\n");
    }
    else {
        printf("[real] thread-private value doesn't match 42 in real mode\n");
    }

    if (tid == pthread_self()) {
        printf("[real] thread_id matches %d\n", (int)tid);
    }
    else {
        printf("[real] thread_id[%d] doesn't match %d\n",
               (int)pthread_self(), (int)tid);
    }

    goto_sim();
    Sim_Print1("back in simulation tid(%i)\n", (int)pthread_self());
    value = pthread_getspecific(key);
    assert(value != NULL);
    if (*((int*)value) == 42) {
        Sim_Print0("[sim] thread-private value maches 42\n");
    }
    else {
        Sim_Print0("[sim] thread-private value doesn't match 42 in real mode\n");
    }

    if (tid == pthread_self()) {
        Sim_Print1("[sim] thread_id matches %d\n", (int)tid);
    }
    else {
        Sim_Print2("[sim] thread_id[%d] doesn't match %d\n", 
                   (int)pthread_self(), (int)tid);
    }

    goto_real();
    
    int private_val2=43;
    pthread_setspecific(key, &private_val2);
    value = pthread_getspecific(key);
    if (*((int*)value) == 43) {
        printf("[real] thread-private value maches 43\n");
    }
    else {
        printf("[real] thread-private value doesn't match 43 in real mode\n");
    }

    if (tid == pthread_self()) {
        printf("[real] thread_id matches %d\n", (int)tid);
    }
    else {
        printf("[real] thread_id[%d] doesn't match %d\n", 
               (int)pthread_self(), (int)tid);
    }

    goto_sim();
    return NULL;
}

void mainX(int argc, const char** const argv, const char **envp) {
    run_test(NULL);

    pthread_t pth;
    Sim_Print0("creating thread\n");
    pthread_create(&pth, NULL, run_test, NULL);
    pthread_join(pth, NULL);
}
